package com.datasentinel.j3d.controls.geom;

/*
 * $RCSfile: Box.java,v $
 *
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or
 * intended for use in the design, construction, operation or
 * maintenance of any nuclear facility.
 *
 * $Revision: 1.6 $
 * $Date: 2007/04/24 18:50:59 $
 * $State: Exp $
 */


import javax.media.j3d.Appearance;
import javax.media.j3d.Group;
import javax.media.j3d.Material;
import javax.media.j3d.Node;
import javax.media.j3d.NodeComponent;
import javax.media.j3d.Shape3D;
import javax.vecmath.Color3f;
import javax.vecmath.Vector3f;


public class ButtonBox  extends Group {

	
	  /**
	   * Specifies that normals are generated along with the positions. 
	   */
	  public static final int GENERATE_NORMALS =  0x01;

	  /**
	   * Specifies that texture coordinates are generated along with the
	   * positions. 
	   */
	  public static final int GENERATE_TEXTURE_COORDS = 0x02;

	  /**
	   * Specifies that normals are to be flipped along the surface.
	   */
	  public static final int GENERATE_NORMALS_INWARD = 0x04;

	  /**
	   * Specifies that texture coordinates are to be Y up.
	   *
	   * @since Java 3D 1.5.1
	   */
	  // Fix to Issue 411. Java 3D prefers images used for texture mapping to be Y-up   
	  public static final int GENERATE_TEXTURE_COORDS_Y_UP = 0x08;
	
	  
	  
	  /** 
	   * Specifies that the geometry being created will not be shared by
	   * another scene graph node. By default all primitives created with
	   * the same parameters share their geometry (e.g., you can have 50
	   * spheres in your scene, but the geometry is stored only once). A
	   * change to one primitive will effect all shared nodes.  You
	   * specify this flag if you do not wish to share any geometry among
	   * primitives of the same parameters.  */
	  public static final int GEOMETRY_NOT_SHARED = 0x10;

	  /**
	   * Specifies that the ALLOW_INTERSECT
	   * capability bit should be set on the generated geometry.
	   * This allows the object
	   * to be picked using Geometry based picking.
	   */
	  public static final int ENABLE_GEOMETRY_PICKING = 0x20;

	  /**
	   * Specifies that the ALLOW_APPEARANCE_READ and 
	   * ALLOW_APPEARANCE_WRITE bits are to be set on the generated
	   * geometry's Shape3D nodes.
	   */
	  public static final int ENABLE_APPEARANCE_MODIFY = 0x40;
  
	  

	public static final String BUTTON_FRONT_SHAPE_NAME = "ButtonBoxFront";
	
  /**
   * Used to designate the front side of the box when using
   * getShape().
   *
   * @see Box#getShape
   */
  public static final int FRONT = 0;

  /**
   * Used to designate the back side of the box when using
   * getShape().
   *
   * @see Box#getShape
   */
  public static final int BACK = 1;

  /**
   * Used to designate the right side of the box when using
   * getShape().
   *
   * @see Box#getShape
   */
  public static final int RIGHT = 2;

  /**
   * Used to designate the left side of the box when using
   * getShape().
   *
   * @see Box#getShape
   */
  public static final int LEFT = 3;

  /**
   * Used to designate the top side of the box when using
   * getShape().
   *
   * @see Box#getShape
   */
  public static final int TOP = 4;

  /**
   * Used to designate the bottom side of the box when using
   * getShape().
   *
   * @see Box#getShape
   */
  public static final int BOTTOM = 5;

  float xDim, yDim, zDim;
  
  protected int numTexUnit = 1;
  protected int numTris = 0;
  protected int numVerts = 0;
  
  private float height = 0.2f;
  private float insetWidth = 0.2f;

  protected int flags;
  
/**
   * Constructs a default box of 1.0 in all dimensions. 
   * Normals are generated by default, texture coordinates are not.
   */

  public ButtonBox()
  {
//    this(1.0f, 1.0f, 1.0f, 0.1f, GENERATE_NORMALS, null);
	 this(1.0f, 1.0f, 1.0f, 0.2f,
		GENERATE_NORMALS | ENABLE_GEOMETRY_PICKING | ENABLE_APPEARANCE_MODIFY, null);	  
  }
  
  

  /** 
   * Constructs a box of a given dimension and appearance.
   * Normals are generated by default, texture coordinates are not.
   *
   * @param xdim X-dimension size.
   * @param ydim Y-dimension size.
   * @param zdim Z-dimension size.
   * @param ap Appearance
   */

//  private ButtonBox(float xdim, float ydim, float zdim,  Appearance ap)
//  {
//    this(xdim, ydim, zdim, 0.1f, GENERATE_NORMALS, ap);
//  }

  /** 
   * Constructs a box of a given dimension, flags, and appearance.
   *
   * @param xdim X-dimension size.
   * @param ydim Y-dimension size.
   * @param zdim Z-dimension size.
   * @param primflags primitive flags.
   * @param ap Appearance
   */
  
  private ButtonBox(float xdim, float ydim, float zdim, float insetWidth, int primflags,
	     Appearance ap, int numTexUnit) {
    
	  
	int i;
    double sign;

    xDim = xdim;
    yDim = ydim;
    zDim = zdim;
    flags = primflags;
    this.insetWidth = insetWidth;
    constructVertices(insetWidth);
    
    boolean texCoordYUp = (flags & GENERATE_TEXTURE_COORDS_Y_UP) != 0;
    
    //Depending on whether normal inward bit is set.
    if ((flags & GENERATE_NORMALS_INWARD) != 0)
        sign = -1.0;
    else sign = 1.0;
    
    
//     TransformGroup objTrans = new TransformGroup();
//     objTrans.setCapability(ALLOW_CHILDREN_READ);
//     this.addChild(objTrans);

    Shape3D shape[] = new Shape3D[6];

//    GeomBuffer cache = null;
    
    for (i = FRONT; i <= BOTTOM; i++){

//	cache = getCachedGeometry(Primitive.BOX, xdim, ydim, zdim, i, i,
//				  primflags);
//	if (cache != null) {
//// 	    System.out.println("using cached geometry i = " + i);
//	    shape[i] = new Shape3D(cache.getComputedGeometry());
//	    numVerts += cache.getNumVerts();
//	    numTris += cache.getNumTris();
//	}
//	else {

	    GeomBuffer gbuf = new GeomBuffer(4, numTexUnit);
	    
	    gbuf.begin(GeomBuffer.QUAD_STRIP);
	    for (int j = 0; j < 2; j++){
		gbuf.normal3d( (double) normals[i].x*sign, 
			       (double) normals[i].y*sign, 
			       (double) normals[i].z*sign);
                if (texCoordYUp) {
                   gbuf.texCoord2d(tcoords[i*8 + j*2], 1.0 - tcoords[i*8 + j*2 + 1]);                    
                }
                else {
                    gbuf.texCoord2d(tcoords[i*8 + j*2], tcoords[i*8 + j*2 + 1]);
                }
                
		gbuf.vertex3d( (double) verts[i*12 + j*3]*xdim, 
			       (double) verts[i*12+ j*3 + 1]*ydim,
			       (double) verts[i*12+ j*3 + 2]*zdim );
	    }
	    for (int j = 3; j > 1; j--){
		gbuf.normal3d( (double) normals[i].x*sign, 
			       (double) normals[i].y*sign, 
			       (double) normals[i].z*sign);
                if (texCoordYUp) {
                    gbuf.texCoord2d(tcoords[i*8 + j*2], 1.0 - tcoords[i*8 + j*2 + 1]);                    
                }
                else {
                    gbuf.texCoord2d(tcoords[i*8 + j*2], tcoords[i*8 + j*2 + 1]);
                }
		gbuf.vertex3d( (double) verts[i*12 + j*3]*xdim, 
			       (double) verts[i*12+ j*3 + 1]*ydim,
			       (double) verts[i*12+ j*3 + 2]*zdim );
	    }
	    gbuf.end();
	    shape[i] = new Shape3D(gbuf.getGeom(flags));
	    numVerts = gbuf.getNumVerts();
	    numTris = gbuf.getNumTris();

//	    if  ((primflags & Primitive.GEOMETRY_NOT_SHARED) == 0) {
//		cacheGeometry(Primitive.BOX, xdim, ydim, zdim, i, i,
//			      primflags, gbuf);
//	    }
//	}

      if ((flags & ENABLE_APPEARANCE_MODIFY) != 0) {
	  (shape[i]).setCapability(Shape3D.ALLOW_APPEARANCE_READ);
	  (shape[i]).setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
      }

      if ((flags & ENABLE_GEOMETRY_PICKING) != 0) {
          (shape[i]).setCapability(Shape3D.ALLOW_GEOMETRY_READ);
      }

//       objTrans.addChild(shape[i]);
      this.addChild(shape[i]);
    }
    
    shape[FRONT].setUserData(BUTTON_FRONT_SHAPE_NAME);
    
    if (ap == null){
      setAppearance();
    }
    else setAppearance(ap);
  }
  
  

   
  public ButtonBox(float xdim, float ydim, float zdim, float insetWidth, int primflags,
	     Appearance ap) {
    this(xdim, ydim, zdim, insetWidth, primflags, ap, 1);
  }

  
  public float getHeight()
  {
  	return height;
  }

  
  /**
   *  Gets one of the faces (Shape3D) from the box that contains the
   *  geometry and appearance. This allows users to modify the 
   *  appearance or geometry of individual parts. 
   * @param partId The part to return.
   * @return The Shape3D object associated with the partID.  If an
   * invalid partId is passed in, null is returned.
   */

  public Shape3D getShape(int partId) {
    if ((partId >= FRONT) && (partId <= BOTTOM))
// 	return (Shape3D)(((Group)getChild(0)).getChild(partId));
	return (Shape3D)getChild(partId);
    return null;
  }
  
  /**
   *  Sets appearance of the box. This will set each face of the
   *  box to the same appearance. To set each face's appearance 
   *  separately, use getShape(partId) to get the
   *  individual shape and call shape.setAppearance(ap). 
   */
  
  public void setAppearance(Appearance ap){
//     ((Shape3D)((Group)getChild(0)).getChild(TOP)).setAppearance(ap);
//     ((Shape3D)((Group)getChild(0)).getChild(LEFT)).setAppearance(ap);
//     ((Shape3D)((Group)getChild(0)).getChild(RIGHT)).setAppearance(ap);
//     ((Shape3D)((Group)getChild(0)).getChild(FRONT)).setAppearance(ap);
//     ((Shape3D)((Group)getChild(0)).getChild(BACK)).setAppearance(ap);
//     ((Shape3D)((Group)getChild(0)).getChild(BOTTOM)).setAppearance(ap);
      ((Shape3D)getChild(TOP)).setAppearance(ap);
      ((Shape3D)getChild(LEFT)).setAppearance(ap);
      ((Shape3D)getChild(RIGHT)).setAppearance(ap);
      ((Shape3D)getChild(FRONT)).setAppearance(ap);
      ((Shape3D)getChild(BACK)).setAppearance(ap);
      ((Shape3D)getChild(BOTTOM)).setAppearance(ap);
  }

  /** Sets the main appearance of the primitive (all subparts) to 
   *  a default white appearance.
   */
  public void setAppearance(){

    Color3f aColor  = new Color3f(0.1f, 0.1f, 0.1f);
    Color3f eColor  = new Color3f(0.0f, 0.0f, 0.0f);
    Color3f dColor  = new Color3f(0.6f, 0.6f, 0.6f);
    Color3f sColor  = new Color3f(1.0f, 1.0f, 1.0f);

    Material m = new Material(aColor, eColor, dColor, sColor, 100.0f);
    Appearance a = new Appearance();
    m.setLightingEnable(true);
    a.setMaterial(m);
    setAppearance(a);
  }
  
    /**
     * Gets the appearance of the specified part of the box.
     *
     * @param partId identifier for a given subpart of the box
     *
     * @return The appearance object associated with the partID.  If an
     * invalid partId is passed in, null is returned.
     *
     * @since Java 3D 1.2.1
     */
    public Appearance getAppearance(int partId) {
	if (partId > BOTTOM || partId < FRONT) return null;
	return getShape(partId).getAppearance();
    }

    private void constructVertices(float insetSize)
    {
    	float ivx = 1.0f - insetSize / 1.5f;
    	float ivy = 1.0f - insetSize;
    	
    	
    	verts = new  float[] {
    		    // front face
    		    ivx, -ivy,  height,	
    		    ivx,  ivy,  height, 
    		   -ivx,  ivy,  height, 
    		   -ivx, -ivy,  height,
    		    // back face
    		-1.0f, -1.0f, 0.0f,
    		-1.0f,  1.0f, 0.0f,
    		    1.0f,  1.0f, 0.0f,
    		    1.0f, -1.0f, 0.0f,
    		    // right face
    		    1.0f, -1.0f, 0.0f,
    		    1.0f,  1.0f, 0.0f,
    		    ivx,  ivy,  height,
    		    ivx, -ivy,  height, 
    		    // left face
    		    -ivx, -ivy,  height, 
    		-ivx,  ivy,  height,  
    		-1.0f,  1.0f, 0.0f,
    		-1.0f, -1.0f, 0.0f,
    		    // top face
    			ivx,  ivy,  height, 
    		    1.0f,  1.0f, 0.0f,
    		-1.0f,  1.0f, 0.0f,
    		-ivx,  ivy,  height, 
    		    // bottom face
    		-ivx, -ivy,  height, 
    		-1.0f, -1.0f, 0.0f,
    		    1.0f, -1.0f, 0.0f,
    		    ivx, -ivy,  height,
    		  };
    }
    
    
  private  float[] verts = {
    // front face
    0.6f, -0.6f,  0.6f,	
    0.6f,  0.6f,  0.6f, 
   -0.6f,  0.6f,  0.6f, 
   -0.6f, -0.6f,  0.6f,
    // back face
-1.0f, -1.0f, 0.0f,
-1.0f,  1.0f, 0.0f,
    1.0f,  1.0f, 0.0f,
    1.0f, -1.0f, 0.0f,
    // right face
    1.0f, -1.0f, 0.0f,
    1.0f,  1.0f, 0.0f,
    0.6f,  0.6f,  0.6f,
    0.6f, -0.6f,  0.6f, 
    // left face
    -0.6f, -0.6f,  0.6f, 
-0.6f,  0.6f,  0.6f,  
-1.0f,  1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
    // top face
	0.6f,  0.6f,  0.6f, 
    1.0f,  1.0f, 0.0f,
-1.0f,  1.0f, 0.0f,
-0.6f,  0.6f,  0.6f, 
    // bottom face
-0.6f, -0.6f,  0.6f, 
-1.0f, -1.0f, 0.0f,
    1.0f, -1.0f, 0.0f,
    0.6f, -0.6f,  0.6f,
  };
  
  private static final double[] tcoords = {
    // front
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
    0.0, 0.0,
    // back
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
    0.0, 0.0,
    //right
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
    0.0, 0.0,
    // left
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
    0.0, 0.0,
    // top
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0,
    0.0, 0.0,
    // bottom
    0.0, 1.0,
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0
  };
  
  
  private static final Vector3f[] normals = {
    new Vector3f( 0.0f,  0.0f,  1.0f),	// front face
    new Vector3f( 0.0f,  0.0f, -1.0f),	// back face
    new Vector3f( 1.0f,  0.0f,  0.0f),	// right face
    new Vector3f(-1.0f,  0.0f,  0.0f),	// left face
    new Vector3f( 0.0f,  1.0f,  0.0f),	// top face
    new Vector3f( 0.0f, -1.0f,  0.0f),	// bottom face
  };
  

    /**
     * Used to create a new instance of the node.  This routine is called
     * by <code>cloneTree</code> to duplicate the current node.
     * <code>cloneNode</code> should be overridden by any user subclassed
     * objects.  All subclasses must have their <code>cloneNode</code>
     * method consist of the following lines:
     * <P><blockquote><pre>
     *     public Node cloneNode(boolean forceDuplicate) {
     *         UserSubClass usc = new UserSubClass();
     *         usc.duplicateNode(this, forceDuplicate);
     *         return usc;
     *     }
     * </pre></blockquote>
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> variable determines whether
     *  NodeComponent data is duplicated or copied.
     *
     * @see Node#cloneTree
     * @see Node#duplicateNode
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    public Node cloneNode(boolean forceDuplicate) {
        ButtonBox b = new ButtonBox(xDim, yDim, zDim, this.insetWidth, getPrimitiveFlags(), getAppearance());
        b.duplicateNode(this, forceDuplicate);
        return b;
    }

    /**
     * Copies all node information from <code>originalNode</code> into
     * the current node.  This method is called from the
     * <code>cloneNode</code> method which is, in turn, called by the
     * <code>cloneTree</code> method.
     * <P>
     * For any <i>NodeComponent</i> objects
     * contained by the object being duplicated, each <i>NodeComponent</i>
     * object's <code>duplicateOnCloneTree</code> value is used to determine
     * whether the <i>NodeComponent</i> should be duplicated in the new node
     * or if just a reference to the current node should be placed in the
     * new node.  This flag can be overridden by setting the
     * <code>forceDuplicate</code> parameter in the <code>cloneTree</code>
     * method to <code>true</code>.
     *
     * @param originalNode the original node to duplicate.
     * @param forceDuplicate when set to <code>true</code>, causes the
     *  <code>duplicateOnCloneTree</code> flag to be ignored.  When
     *  <code>false</code>, the value of each node's
     *  <code>duplicateOnCloneTree</code> variable determines whether
     *  NodeComponent data is duplicated or copied.
     *
     * @see Node#cloneTree
     * @see Node#cloneNode
     * @see NodeComponent#setDuplicateOnCloneTree
     */
    public void duplicateNode(Node originalNode, boolean forceDuplicate) {
        super.duplicateNode(originalNode, forceDuplicate);
    }

    /**
     * Returns the X-dimension size of the Box
     *
     * @since Java 3D 1.2.1
     */
    public float getXdimension() {
	return xDim;
    }

    /**
     * Returns the Y-dimension size of the Box
     *
     * @since Java 3D 1.2.1
     */
    public float getYdimension() {
	return yDim;
    }

    /**
     * Returns the Z-dimension size of the Box
     *
     * @since Java 3D 1.2.1
     */
    public float getZdimension() {
	return zDim;
    }
    
    /** Gets the appearance of the primitive (defaults to first subpart).
     */
    public Appearance getAppearance(){
      return getShape(0).getAppearance();
    }

    /** Returns the flags of primitive (generate normal, textures, caching, etc).
     */
    public int getPrimitiveFlags()
    {
      return flags;
    }
}

